using UnityEngine;
using UnityEditor;
using System;
using System.Collections;
using System.Collections.Generic;

namespace Obi
{

    public class ObiPathHandles
    {

        static int splineSelectorHash = "ObiPathSelectorHash".GetHashCode();
        const int minSelectionDistance = 5;

        static Vector2 startPos;
        static Vector2 currentPos;
        static bool dragging = false;
        static Rect marquee;

        public static bool SplineCPSelector(ObiPath path, bool[] selectionStatus)
        {

            int controlID = GUIUtility.GetControlID(splineSelectorHash, FocusType.Passive);
            int selectedCPIndex = -1;
            bool selectionStatusChanged = false;

            // select vertex on mouse click:
            switch (Event.current.GetTypeForControl(controlID))
            {
                case EventType.Layout:
                case EventType.MouseMove:
                    
                        float minSqrDistance = System.Single.MaxValue;
                        float sqrMinSelectionDistance = minSelectionDistance * minSelectionDistance;

                        for (int i = 0; i < path.ControlPointCount; i++)
                        {

                            // get particle position in gui space:
                            Vector2 pos = HandleUtility.WorldToGUIPoint(path.points[i].position);

                            // get distance from mouse position to particle position:
                            float sqrDistance = Vector2.SqrMagnitude(Event.current.mousePosition - pos);

                            // check if this control point is closer to the cursor that any previously considered point.
                            if (sqrDistance < sqrMinSelectionDistance && sqrDistance < minSqrDistance)
                            { 
                                minSqrDistance = sqrDistance;
                            }

                        }
                        HandleUtility.AddControl(controlID, Mathf.Sqrt(minSqrDistance));
                    
                    break;

                case EventType.MouseDown:
                    
                        marquee.Set(0, 0, 0, 0);
                        startPos = Event.current.mousePosition;

                        if (Event.current.button == 0)
                        {

                            if (HandleUtility.nearestControl == controlID)
                            {
                                GUIUtility.hotControl = controlID;

                                // If the user is pressing shift or ctrl, accumulate selection.
                                if ((Event.current.modifiers & (EventModifiers.Shift | EventModifiers.Control)) == 0 && (Event.current.modifiers & EventModifiers.Alt) == 0)
                                {
                                    for (int i = 0; i < selectionStatus.Length; i++)
                                        selectionStatus[i] = false;

                                    selectionStatusChanged = true;
                                }

                                minSqrDistance = System.Single.MaxValue;
                                sqrMinSelectionDistance = minSelectionDistance * minSelectionDistance;

                                for (int i = 0; i < path.ControlPointCount; i++)
                                {

                                    // get particle position in gui space:
                                    Vector2 pos = HandleUtility.WorldToGUIPoint(path.points[i].position);

                                    // get distance from mouse position to particle position:
                                    float sqrDistance = Vector2.SqrMagnitude(startPos - pos);

                                    // check if this control point is closer to the cursor that any previously considered point.
                                    if (sqrDistance < sqrMinSelectionDistance && sqrDistance < minSqrDistance)
                                    {
                                        minSqrDistance = sqrDistance;
                                        selectedCPIndex = i;
                                    }

                                }

                                if (selectedCPIndex >= 0)
                                { // toggle particle selection status.

                                    selectionStatus[selectedCPIndex] = !selectionStatus[selectedCPIndex];
                                    selectionStatusChanged = true;

                                    // Prevent spline deselection if we have selected a particle:
                                    Event.current.Use();

                                }
                            }
                            else if ((Event.current.modifiers & (EventModifiers.Shift | EventModifiers.Control)) == 0 && (Event.current.modifiers & EventModifiers.Alt) == 0)
                            {
                                for (int i = 0; i < selectionStatus.Length; i++)
                                    selectionStatus[i] = false;

                                selectionStatusChanged = true;
                            }
                        }
                    
                    break;

                case EventType.MouseDrag:

                    if (Event.current.button == 0 && (Event.current.modifiers & EventModifiers.Alt) == 0)
                    {
                        currentPos = Event.current.mousePosition;
                        if (!dragging && Vector2.Distance(startPos, currentPos) > 5)
                        {
                            dragging = true;
                        }

                        if (dragging)
                        {
                            GUIUtility.hotControl = controlID;
                            Event.current.Use();
                        }

                        //update marquee rect:
                        float left = Mathf.Min(startPos.x, currentPos.x);
                        float right = Mathf.Max(startPos.x, currentPos.x);
                        float bottom = Mathf.Min(startPos.y, currentPos.y);
                        float top = Mathf.Max(startPos.y, currentPos.y);

                        marquee = new Rect(left, bottom, right - left, top - bottom);

                    }

                    break;

                case EventType.MouseUp:

                    if (GUIUtility.hotControl == controlID)
                    {

                        dragging = false;

                        for (int i = 0; i < path.ControlPointCount; i++)
                        {

                            // get particle position in gui space:
                            Vector2 pos = HandleUtility.WorldToGUIPoint(path.points[i].position);

                            if (pos.x > marquee.xMin && pos.x < marquee.xMax && pos.y > marquee.yMin && pos.y < marquee.yMax)
                            {
                                selectionStatus[i] = true;
                                selectionStatusChanged = true;
                            }

                        }

                        GUIUtility.hotControl = 0;
                        Event.current.Use();
                    }

                    break;

                case EventType.Repaint:

                    if (dragging)
                    {
                        GUISkin oldSkin = GUI.skin;
                        GUI.skin = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene);
                        Handles.BeginGUI();
                        GUI.Box(new Rect(marquee.xMin, marquee.yMin, marquee.width, marquee.height), "");
                        Handles.EndGUI();
                        GUI.skin = oldSkin;
                    }

                    break;

            }

            return selectionStatusChanged;
        }

        private static void DrawControlPointArcs(ObiPath path, float thicknessScale)
        {
            for (int i = 0; i < path.ControlPointCount; ++i)
            {
                Vector3 position = path.points[i].position;
                Vector3 tangent = path.points.GetTangent(i);
                Vector3 right = Vector3.Cross(tangent, path.normals[i]).normalized;
                float thickness = path.thicknesses[i] * thicknessScale + 0.05f;

                Handles.DrawWireArc(position, tangent, right, -180, thickness);
            }
        }

        private static void DrawPathPolylines(Vector3[] samples, Vector3[] leftSamples, Vector3[] rightSamples, Vector3[] upSamples, bool drawOrientation)
        {
            Handles.DrawPolyLine(samples);
            if (drawOrientation)
            {
                Handles.DrawPolyLine(leftSamples);
                Handles.DrawPolyLine(upSamples);
                Handles.DrawPolyLine(rightSamples);
            }
        }

        public static void DrawPathHandle(ObiPath path, Matrix4x4 referenceFrame, float thicknessScale, int resolution, bool drawOrientation = true)
        {

            if (path == null || path.GetSpanCount() == 0) return;

            Matrix4x4 prevMatrix = Handles.matrix;
            Handles.matrix = referenceFrame;

            // Draw the curve:
            int curveSegments = path.GetSpanCount() * resolution;
            Vector3[] samples = new Vector3[curveSegments + 1];
            Vector3[] leftSamples = new Vector3[curveSegments + 1];
            Vector3[] rightSamples = new Vector3[curveSegments + 1];
            Vector3[] upSamples = new Vector3[curveSegments + 1];

            for (int i = 0; i <= curveSegments; ++i)
            {

                float mu = i / (float)curveSegments;
                samples[i] = path.points.GetPositionAtMu(path.Closed,mu);

                if (drawOrientation)
                {
                    Vector3 tangent = path.points.GetTangentAtMu(path.Closed,mu);
                    Vector3 right = Vector3.Cross(tangent, path.normals.GetAtMu(path.Closed,mu)).normalized;
                    Vector3 up = Vector3.Cross(right, tangent).normalized;
                    float thickness = path.thicknesses.GetAtMu(path.Closed,mu) * thicknessScale + 0.05f;

                    leftSamples[i] = samples[i] - right * thickness;
                    rightSamples[i] = samples[i] + right * thickness;
                    upSamples[i] = samples[i] + up * thickness;

                    if (i % 5 == 0)
                    {
                        Handles.DrawLine(leftSamples[i], rightSamples[i]);
                        Handles.DrawLine(samples[i], samples[i] + up * thickness);
                    }
                }
            }

            if (drawOrientation)
                DrawControlPointArcs(path, thicknessScale);

            DrawPathPolylines(samples, leftSamples, rightSamples, upSamples, drawOrientation);
            DrawPathPolylines(samples, leftSamples, rightSamples, upSamples, drawOrientation);

            Handles.matrix = prevMatrix;
        }


    }
}

